package com.voice.core.api.client;

import com.voice.core.api.enums.HttpMethod;
import com.voice.core.api.exception.SdkException;
import com.voice.core.api.model.ApiRequest;
import com.voice.core.api.model.ApiResponse;
import com.voice.core.api.model.HttpClientBuilderParams;
import com.voice.core.api.utils.ApiRequestMaker;
import com.voice.core.api.utils.HttpCommonUtil;
import com.voice.core.utils.CloseUtil;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;


public class ApacheHttpClient extends BaseApiClient {
    private CloseableHttpClient httpClient;
    private PoolingHttpClientConnectionManager connectionManager;

    protected ApacheHttpClient() {
    }

    public void init(HttpClientBuilderParams params) {
        params.check();
        SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).setSoKeepAlive(true).setSoReuseAddress(true).setSoTimeout(params.getReadTimeout()).build();
        Registry<ConnectionSocketFactory> registry = getRegistry();
        if (params.getRegistry() != null) registry = params.getRegistry();

        this.connectionManager = new PoolingHttpClientConnectionManager(registry);
        this.connectionManager.setDefaultConnectionConfig(ConnectionConfig.custom().build());
        this.connectionManager.setDefaultSocketConfig(socketConfig);
        this.connectionManager.setMaxTotal(params.getDispatchMaxRequests());
        this.connectionManager.setDefaultMaxPerRoute(params.getDispatchMaxRequestsPerHost());
        RequestConfig defaultConfig = RequestConfig.custom().setConnectTimeout(params.getConnectionTimeout()).setSocketTimeout(params.getReadTimeout()).setConnectionRequestTimeout(params.getReadTimeout()).build();
        this.httpClient = HttpClients.custom().setConnectionManager(this.connectionManager).setDefaultRequestConfig(defaultConfig).build();
        this.appKey = params.getAppKey();
        this.appSecret = params.getAppSecret();
        this.host = params.getHost();
        this.scheme = params.getScheme();
    }

    private static Registry<ConnectionSocketFactory> getRegistry() {
        RegistryBuilder registryBuilder = RegistryBuilder.create();
        try {
            registryBuilder.register("http", PlainConnectionSocketFactory.INSTANCE).build();
            registryBuilder.register("https", new SSLConnectionSocketFactory(SSLContext.getDefault(), new DefaultHostnameVerifier()));
        } catch (Exception var2) {
            throw new RuntimeException("HttpClientUtil init failure !", var2);
        }
        return registryBuilder.build();
    }

    private static Registry<ConnectionSocketFactory> getNoVerifyRegistry() {
        RegistryBuilder registryBuilder = RegistryBuilder.create();
        try {
            registryBuilder.register("http", PlainConnectionSocketFactory.INSTANCE).build();
            registryBuilder.register("https", new SSLConnectionSocketFactory((new SSLContextBuilder()).loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()), (X509Certificate[] chain, String authType)-> true).build(),
                    (String hostname, SSLSession session)-> true));
        } catch (Exception var2) {
            throw new RuntimeException("HttpClientUtil init failure !", var2);
        }

        return registryBuilder.build();
    }

    private HttpUriRequest buildRequest(ApiRequest apiRequest) {
        RequestBuilder builder = RequestBuilder.create(apiRequest.getMethod().getValue());
        try {
            URIBuilder uriBuilder = new URIBuilder();
            uriBuilder.setScheme(apiRequest.getScheme().name());
            uriBuilder.setHost(apiRequest.getHost());
            uriBuilder.setPath(apiRequest.getPath());
            HttpCommonUtil.mapConsume(apiRequest.getQuerys(),(isFirst,entry)->uriBuilder.addParameter(entry.getKey(),entry.getValue()));
            builder.setUri(uriBuilder.build());
        } catch (URISyntaxException var8) {
            throw new SdkException("build http request uri failed", var8);
        }

        if(apiRequest.getMethod()== HttpMethod.POST_FORM_DATA){
            MultipartEntityBuilder multipartEntityBuilder=MultipartEntityBuilder.create();
            multipartEntityBuilder.setContentType(ContentType.parse(apiRequest.getMethod().getRequestContentType()));
            apiRequest.getFiles().forEach((key,file)->multipartEntityBuilder.addBinaryBody(key,file));
            apiRequest.getFormParams().forEach((key,value)-> multipartEntityBuilder.addTextBody(key,value));
            builder.setEntity(multipartEntityBuilder.build());
            apiRequest.addHeader("content-type", builder.getEntity().getContentType().getValue());
        }else{
            EntityBuilder bodyBuilder = EntityBuilder.create();
            bodyBuilder.setContentType(ContentType.parse(apiRequest.getMethod().getRequestContentType()));
            if (!HttpCommonUtil.isEmpty(apiRequest.getFormParams())) {
                List<NameValuePair> paramList = new ArrayList();
                HttpCommonUtil.mapConsume(apiRequest.getFormParams(),(isFirst,entry)->
                        paramList.add(new BasicNameValuePair(entry.getKey(),entry.getValue())));
                bodyBuilder.setParameters(paramList);
                builder.setEntity(bodyBuilder.build());
                builder.setEntity(bodyBuilder.build());
            } else if (!HttpCommonUtil.isEmpty(apiRequest.getBody())) {
                bodyBuilder.setBinary(apiRequest.getBody());
                builder.setEntity(bodyBuilder.build());
            }
        }

        ApiRequestMaker.make(apiRequest, this.appKey, this.appSecret);
        HttpCommonUtil.mapConsume(apiRequest.getHeaders(),(isFirst,entry)->
                entry.getValue().forEach(value->builder.addHeader(entry.getKey(),value)));
        return builder.build();
    }

    private ApiResponse parseToApiResponse(HttpResponse httpResponse) throws IOException {
        ApiResponse result = new ApiResponse(httpResponse.getStatusLine().getStatusCode());
        result.setHeaders(new HashMap());
        Header[] headers = httpResponse.getAllHeaders();
        int length = headers.length;

        for(int i = 0; i < length; ++i) {
            Header header = headers[i];
            List<String> values =result.getHeaders().get(header.getName());
            if (values == null) values = new ArrayList();
            values.add(header.getValue());
            result.getHeaders().put(header.getName().toLowerCase(), values);
        }

        result.setMessage(httpResponse.getStatusLine().getReasonPhrase());
        if (httpResponse.getEntity() != null) {
            Header contentType = httpResponse.getEntity().getContentType();
            if (contentType != null) {
                result.setContentType(contentType.getValue());
            } else {
                result.setContentType("application/text; charset=utf-8");
            }
            result.setBody(EntityUtils.toByteArray(httpResponse.getEntity()));
        } else {
            String contentTypeStr = result.getFirstHeaderValue("content-type");
            if (null == contentTypeStr) {
                contentTypeStr = "application/text; charset=utf-8";
            }
            result.setContentType(contentTypeStr);
        }
        return result;
    }

    public final ApiResponse sendSyncRequest(ApiRequest apiRequest) {
        HttpUriRequest httpRequest = this.buildRequest(apiRequest);
        try {
            CloseableHttpResponse httpResponse = this.httpClient.execute(httpRequest);
            ApiResponse apiResponse = this.parseToApiResponse(httpResponse);
            CloseUtil.close(httpResponse);
            return apiResponse;
        } catch (Exception var8) {
            throw new SdkException(var8);
        }
    }



    public void shutdown() {
        this.connectionManager.shutdown();
        CloseUtil.close(this.httpClient);
    }

}

